package capitulo3;

import java.applet.Applet;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.util.Random;

@SuppressWarnings("serial")
public class Asteroids extends Applet implements Runnable, KeyListener {

	//The main threat becomes the game loop
	Thread gameLoop;
	
	//Use this as a double buffer
	BufferedImage backBuffer;
	
	// The main drawing object for the back buffer
	Graphics2D g2d;
	
	//toggle for drawing bounding boxes
	boolean showBounds = false;
	
	// Create the asteroid array
	int ASTEROIDS = 20;
	Asteroid[] ast = new Asteroid[ASTEROIDS];
	
	// Create the bullet array
	int BULLETS = 10;
	Bullet[] bullet = new Bullet[BULLETS];
	int currentBullet = 0;
	
	// The player's ship
	Ship ship = new Ship();
	
	// Create the identity transform (0,0)
	AffineTransform identity = new AffineTransform();
	
	// Create a random number generator
	Random rand = new Random();
	
	// Applet init event
	public void init() {
		// Create the back buffer for smoth graphics
		backBuffer = new BufferedImage(640, 480, BufferedImage.TYPE_INT_BGR);			// Creamos una imagen
		g2d = backBuffer.createGraphics();												// Creamos un Graphics2D para dibujar en la imagen
		
		// Set up the ship
		ship.setX(320);
		ship.setY(240);
		
		// Set up the bullets
		for (int i=0; i<BULLETS; i++){
			bullet[i] = new Bullet();
		}
		
		//Create the asteriods
		for (int i=0; i<ASTEROIDS; i++){
			ast[i] = new Asteroid();
			ast[i].setRotationVelocity(rand.nextInt(3)+1);
			ast[i].setX((double)rand.nextInt(600)+20);
			ast[i].setY((double)rand.nextInt(440)+20);
			ast[i].setMoveAngle(rand.nextInt(360));
			double ang = ast[i].getMoveAngle() - 90;
			ast[i].setVelX(calcAngleMoveX(ang));
			ast[i].setVelY(calcAngleMoveY(ang));
		}
		
		// Start the user input Listener
		addKeyListener(this);
	}
	
	// Applet update event to redraw the screen
	public void update(Graphics g){
		// start off transform at identity
		g2d.setTransform(identity);
		
		// Erase the background
		g2d.setColor(Color.BLACK);
		g2d.fillRect(0, 0, getSize().width, getSize().height);
		
		// Print some status information
		g2d.setColor(Color.WHITE);
		g2d.drawString("Ship: " + Math.round(ship.getX()) + "," + Math.round(ship.getY()), 5, 10);
		g2d.drawString("Move angle: " + Math.round(ship.getMoveAngle())+90, 5, 25);
		g2d.drawString("Face angle: " + Math.round(ship.getFaceAngle()), 5,40);
		g2d.drawString("Velocity: " + Math.round(ship.getVelX()) + "," + Math.round(ship.getVelY()),5, 55);
		
		// draw the game graphics
		drawShip();
		drawBullets();
		drawAsteroids();
		
		// Repaint the applet Window
		paint(g);
	}
	
	// applet window repaint event - draw the back buffer (this method is called by update()
	public void paint(Graphics g){
		// draw the back buffer onto the applet window
		g.drawImage(backBuffer,0,0,this);
	}
	
	// drawShip called by applet update event
	private void drawShip(){
		g2d.setTransform(identity);
		g2d.translate(ship.getX(), ship.getY());
		g2d.rotate(Math.toRadians(ship.getFaceAngle()));
		g2d.setColor(Color.ORANGE);
		g2d.fill(ship.getShape());
	}
	
	// drawBullets called by applet update event
	private void drawBullets(){
		// iterate through the array of bullets
		for (int i=0; i<BULLETS; i++){
		    // Is this bullet currently in use?
			if(bullet[i].isAlive()){
				g2d.setTransform(identity);
				g2d.translate(bullet[i].getX(), bullet[i].getY());
				g2d.setColor(Color.MAGENTA);
				g2d.fill(bullet[i].getShape());
			}
		}
	}
	
	// drawAsteroids called by applet update ev
	private void drawAsteroids(){
		// iterate through the array of asteroids
		for (int i=0; i<ASTEROIDS; i++){
		    // Is this bullet currently in use?
			if(ast[i].isAlive()){
				g2d.setTransform(identity);
				g2d.translate(ast[i].getX(), ast[i].getY());
				g2d.rotate(Math.toRadians(ast[i].getMoveAngle()));
				g2d.setColor(Color.DARK_GRAY);
				g2d.fill(ast[i].getShape());
			}
		}
	}
	
	// Thread start event - start the game loop running
	public void start(){
		// Create the gameloop thread for the real-time updates
		gameLoop = new Thread(this);
		gameLoop.start();
	}
	
	// Thread stop event
	public void stop(){
		// kill the gameloop thread
		gameLoop = null;
	}

	// Calculate X movement value based on direction angle
	private double calcAngleMoveX(double angle){
		return (double)(Math.cos(angle*Math.PI/180));
	}
	
	// Calculate Y movement value based on direction angle
	private double calcAngleMoveY(double angle){
		return (double)(Math.sin(angle*Math.PI/180));
	}
	
	// Key Pressed - Keyboard event to move the ship
	@Override
	public void keyPressed(KeyEvent e) {
		int keyCode = e.getKeyCode();
		switch(keyCode){
			case KeyEvent.VK_LEFT:
				// Left arrow rotates ship left 5 degrees
				ship.incFaceAngle(-10);
				if (ship.getFaceAngle() < 0) 
					ship.setFaceAngle(360-10);
				break;
			case KeyEvent.VK_RIGHT:
				// Left arrow rotates ship right 5 degrees
				ship.incFaceAngle(10);
				if (ship.getFaceAngle() > 360) 
					ship.setFaceAngle(10);
				break;
			case KeyEvent.VK_UP:
				// up arrow adds thrust to ship (1/10 normal speed)
				ship.setMoveAngle(ship.getFaceAngle()-90);
				ship.incVelX(calcAngleMoveX(ship.getMoveAngle())*0.1);
				ship.incVely(calcAngleMoveY(ship.getMoveAngle())*0.1);
				break;
			// Ctrl, enter or space can be used to fire weapon
			case KeyEvent.VK_CONTROL:
			case KeyEvent.VK_ENTER:
			case KeyEvent.VK_SPACE:
				// Fire a bullet
				currentBullet++;
				if (currentBullet > BULLETS -1)
					currentBullet=0;
				bullet[currentBullet].setAlive(true);
				
				//point bullet in the same direction ship is facing
				bullet[currentBullet].setX(ship.getX());
				bullet[currentBullet].setY(ship.getY());
				bullet[currentBullet].setMoveAngle(ship.getFaceAngle()-90);
				
				// fire bullet at angle of the ship
				double angle = bullet[currentBullet].getMoveAngle();	
				// Capitulo2 - Ejercicio 2
				//double svx = ship.getVelX();
				//double svy = ship.getVelY();
				//bullet[currentBullet].setVelX(svx+calcAngleMoveX(angle) * 2);
				//bullet[currentBullet].setVelY(svy+calcAngleMoveY(angle) * 2);
				bullet[currentBullet].setVelX(calcAngleMoveX(angle) * 10);
				bullet[currentBullet].setVelY(calcAngleMoveY(angle) * 10);
				break;
		}
	}
	
	// Key Released - not used for this game
	@Override
	public void keyReleased(KeyEvent e) { }

	// Key Released - not used for this game
	@Override
	public void keyTyped(KeyEvent e) { }

	// This is the thread to make the gameloop
	// thread run event (game loop)
	@Override
	public void run() {
		// Acquire the current thread
		Thread t = Thread.currentThread();
		
		// Keep going as long as the thread is alive
		while(t==gameLoop){
			try {
				// update the game loop
				gameUpdate();
				
				// target framerate is 50 fps
				Thread.sleep(20);
			} catch(InterruptedException e) {
				e.printStackTrace();
			}
			repaint();
		}
	}
	
	// This is used to update the objects	

	// Move an animate the objects in the game
	private void gameUpdate(){
		updateShip();
		updateBullets();
		updateAsteroids();
		checkCollisions();
	}
	
	// This method updates the ship movement
	
	// Update the ship position based on velocity
	public void updateShip(){
		// update ship's X position
		ship.incX(ship.getVelX());
		
		// Wrap around left/right
		if (ship.getX() < -10) 
			ship.setX(getSize().width + 10);
		else if (ship.getX() > getSize().width + 10)
			ship.setX(-10);
		
		// update ship's Y position
		ship.incY(ship.getVelY());
		
		// Wrap around up/down
		if (ship.getY() < -10) 
			ship.setY(getSize().height + 10);
		else if (ship.getY() > getSize().height + 10)
			ship.setY(-10);
	}
	
	// This method updates bullets movement
	
	// Update the bullets based on velocity
	public void updateBullets(){
		// move each of the bullets
				for (int i=0; i<BULLETS; i++){
					
					// is this bullet being used?
					if (bullet[i].isAlive()) {
						
						// Update bullet's position
						bullet[i].incX(bullet[i].getVelX());
						
						// Bullet disappears at left/right edge
						if (bullet[i].getX() < 0 || bullet[i].getX() > getSize().width) {
							bullet[i].setAlive(false);
						}
						
						// Update bullet's position
						bullet[i].incY(bullet[i].getVelY());
						
						// Bullet disappears at top/bottom edge
						if (bullet[i].getY() < 0 || bullet[i].getY() > getSize().height) {
							bullet[i].setAlive(false);
						}
					}			
				}		
	}
	
	// // This method updates asteroids movement
	
	// Update the asteroids based on velocity
	public void updateAsteroids(){
		// move each of the asteroids
		for (int i=0; i<ASTEROIDS; i++){
			
			// is this asteroid being used?
			if (ast[i].isAlive()) {
				
				// Update asteroid's position
				ast[i].incX(ast[i].getVelX());
				
				// Warp the asteroid at screen edges
				if (ast[i].getX() < -20 ) {
					ast[i].setX(getSize().width + 20);
				} else if (ast[i].getX() > getSize().width + 20) {
					ast[i].setX(-20);
				}
			
				// Update asteroid's position
				ast[i].incY(ast[i].getVelY());
			
				// Warp the asteroid at screen edges
				if (ast[i].getY() < -20 ) {
					ast[i].setY(getSize().height + 20);
				} else if (ast[i].getY() > getSize().height + 20)
					ast[i].setY(-20);
			}			
		}		
	}
	
	// This method reviews the collision between Asteroids and bullets and Asteroids and ship
	
	// Test asteroids for collisions whit ship or bullets
	public void checkCollisions(){
		
		// iterate through the asteroids array
		for (int m=0; m<ASTEROIDS; m++){
			
			// is this asteroid being used?
			if (ast[m].isAlive()){
				
				// Check the collision with bullet
				for (int n=0; n<BULLETS; n++){
					if (bullet[n].isAlive()){
						
						// perform the collision test
						if (ast[m].getBounds().contains(bullet[n].getX(), bullet[n].getY())){
							bullet[n].setAlive(false);
							ast[m].setAlive(false);
							continue;
						}
					}
				}
				
				// Check for collision with ship
				if (ast[m].getBounds().intersects(ship.getBounds())){
					ast[m].setAlive(false);
					ship.setX(320);
					ship.setY(240);
					ship.setFaceAngle(0);
					ship.setVelX(0);
					ship.setVelY(0);
					continue;
				}
			}
		}
	}
	
}
